package com.welbell.utlis;


import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;

/**
 * @date 2021/1/27 10:59
 */
public class Example {

    /**
     * SHA-256摘要
     * 对encrypt、method、Header请求头的签名，签名采用SHA256算法
     * 签名生成的通用步骤如下：
     * 第一步，设所有发送或者接收到的数据为集合M，将集合M内非空参数值的参数按照参数名ASCII码从小到大排序（字典序），使用URL键值对的格式（即key1=value1&key2=value2…）拼接成字符串stringA。
     * 特别注意以下重要规则：
     * ◆ 参数名ASCII码从小到大排序（字典序）；
     * ◆ 如果参数的值为空不参与签名；
     * ◆ 参数名区分大小写；
     * 第二步，在stringA最后拼接上facilitySecert得到stringSignTemp字符串，并对stringSignTemp进行SHA256运算，得到sign值signValue。
     *
     * @param params         对需要签名的参数封装的Map
     * @param facilitySecert 设备密钥
     * @return
     */
    public static String sha256Sign(Map<String, Object> params, String facilitySecert) {
        StringBuilder sb = new StringBuilder();
        // 将参数以参数名的字典升序排序
        Map<String, Object> sortParams = new TreeMap<String, Object>(params);
        // 遍历排序的字典,并拼接"key=value"格式
        for (Map.Entry<String, Object> entry : sortParams.entrySet()) {
            String key = entry.getKey();
            String value = String.valueOf(entry.getValue()).trim();
            sb.append("&").append(key).append("=").append(value);
        }
        String stringA = sb.toString().replaceFirst("&", "");
        System.out.println(stringA);
        String stringSignTemp = stringA + facilitySecert;
        return Base64.encode(DigestUtil.sha256(stringSignTemp));
    }


    /**
     * AES加密
     *
     * @param json   json字符串
     * @param aesKey aes密钥
     * @return
     */
    public static String aesSign(String json, String aesKey) {
        TLog.d("aesSign: ", json);
        byte[] decode = Base64.decode(aesKey);
        TLog.d("aesSign", "aesSign: " + Arrays.toString(decode));
        return SecureUtil.aes(Base64.decode(aesKey)).encryptBase64(json);
    }

    /**
     * AES解密
     *
     * @param json
     * @param aesKey
     * @return
     */
    public static String aesDecrypt(String json, String aesKey) {
        return SecureUtil.aes(Base64.decode(aesKey)).decryptStr(json);
    }


    /**
     * 通过身份证号码得到赣通码相关信息
     * 生成encrypt参数
     */
    public static String health_code_info(String idCard, String personType, String aesKey) {
        return aesSign("{\"idcard\":\"" + idCard + "\",\"personType\":\"" + personType + "\"}", aesKey);
    }

    /**
     * 通过身份证号码得到赣通码相关信息
     * 生成encrypt参数
     */
    public static String health_code_info(String idCard, String name, String personType, String aesKey) {
        return aesSign("{\"idcard\":\"" + idCard + "\",\"name\":\"" + name + "\",\"personType\":\"" + personType + "\"}", aesKey);
    }


    /**
     * 通过身份证号码得到赣通码相关信息
     * 生成encrypt参数
     */
    public static String wg_health_code_info(String personType, String identification, String aesKey) {
        return aesSign("{\"personType\":\"" + personType + "\",\"identification\":\"" + identification + "\"}", aesKey);
    }

    /**
     * 根据身份证号码得到用户的核酸和抗体检测信息
     * 生成encrypt参数
     */
    public static String health_code_nucleic(String name, String idCard, String length, String aesKey) {
        return aesSign("{\"userName\": \"" + name + "\",\"idCard\": \"" + idCard + "\",\"length\": \"" + length + "\"}", aesKey);
    }

    /**
     * 根据身份证号码得到用户的核酸和抗体检测信息
     * 生成encrypt参数
     */
    public static String health_code_nucleic(String personUuid, String length, String aesKey) {
        return aesSign("{\"personUuid\":\"" + personUuid + "\",\"length\":\"" + length + "\"}", aesKey);
    }

    /**
     * 根据身份证号码得到用户的核酸和抗体检测信息
     * 生成encrypt参数
     */
    public static String health_code_nucleic(String personUuid, String aesKey) {
        return aesSign("{\"personUuid\":\"" + personUuid + "\"}", aesKey);
    }

    /**
     * 通过扫赣通码解析得到二维码相关信息
     * 生成encrypt参数
     */
    public static String analysis_health_code(String ewm, String personType, String aesKey) {
        TLog.d("ewm: ", ewm + "1");
        return aesSign("{\"ewm\":\"" + ewm + "\",\"personType\":\"" + personType + "\"}", aesKey);
    }

    /**
     * 通过赣通码结果获取人证结果
     * 生成encrypt参数
     */
    public static String validate_face(String name, String idcard, String aesKey) {
        return aesSign("{\"idcard\":\"" + idcard + "\",\"name\":\"" + name + "\"}", aesKey);
    }

    public static String validate_face(String identification, String aesKey) {
        return aesSign("{\"identification\":\"" + identification + "\"}", aesKey);
    }


    public static String person_in_out(String personUuid, String temperature, String imageUrl, String videoUrl,
                                       String openType, String inOut, String qRColor, String aesKey) {
        return aesSign("{\"personUuid\":\"" + personUuid + "\",\"temperature\":\"" + temperature + "\"" +
                ",\"pic\":\"" + imageUrl + "\"" +
                ",\"video\":\"" + videoUrl + "\",\"passWay\":\"" + openType + "\",\"inOut\":\"" + inOut + "\",\"color\":\"" + qRColor + "\"}", aesKey);
    }


    /**
     * 门禁得到用户相关信息
     * 生成encrypt参数
     */
    public static String pass_info(String idcard, String temperature, String aesKey) {
        return aesSign("{\n" +
                "\"idCard\": \"" + idcard + "\",\n" +
                "\"temperature\":\"" + temperature + "\"\n" +
                "}\n", aesKey);
    }

    /**
     * 通过二维码获取
     * 生成encrypt参数
     */
    public static String gtm_pass_info(String gtmCode, String temperature, String aesKey) {
        return aesSign("{\n" +
                "    \"gtmCode\":\"" + gtmCode + "\",\n" +
                "    \"temperature\":\"" + temperature + "\"\n" +
                "}\n", aesKey);
    }


    /**
     * 通过人证信息获取
     * 生成encrypt参数
     */
    public static String pass_by_idcard(String idCard, String name, String temperature, String aesKey) {
        return aesSign("{\n" +
                "    \"idCard\":\"" + idCard + "\",\n" +
                "    \"temperature\":\"" + temperature + "\",\n" +
                "    \"name\":\"" + name + "\"\n" +
                "}\n", aesKey);
    }


    /**
     * 上报开门记录
     * 生成encrypt参数
     */
    public static String save_open_info(String openId, String imageBase64, String openStatus, String aesKey) {
        return aesSign("{\n" +
                "    \"openId\":\"" + openId + "\",\n" +
                "    \"openStatus\":\"" + openStatus + "\",\n" +
                "    \"imageBase64\":\"" + imageBase64 + "\"\n" +
                "}", aesKey);
    }


    /**
     * 获取人脸数据
     * 生成encrypt参数
     */
    public static String get_features_file(String facilityId, String aesKey) {
        return aesSign("{\"facilityId\":\"" + facilityId + "\"}", aesKey);
    }


    /*************************************上海码加密解密************************************/


    // 密钥
    private static String charset = "utf-8";
    // 偏移量
    private static int offset = 16;
    private static String transformation = "AES/CBC/PKCS5Padding";
    private static String algorithm = "AES";

    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @param key     加密密码
     * @return
     */
    public static String encrypt(String content, String key) {
        try {
            SecretKeySpec skey = new SecretKeySpec(key.getBytes(), algorithm);
            IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            byte[] byteContent = content.getBytes(charset);
            cipher.init(Cipher.ENCRYPT_MODE, skey, iv);// 初始化
            byte[] result = cipher.doFinal(byteContent);
//            return Base64.getEncoder().encodeToString(result); // 加密
            return Base64.encode(result); // 加密
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES（256）解密
     *
     * @param content 待解密内容
     * @param key     解密密钥
     * @return 解密之后
     * @throws Exception
     */
    public static String decrypt(String content, String key) {
        if (content == null || key == null) {
            return null;
        }
        try {
            SecretKeySpec skey = new SecretKeySpec(key.getBytes(), algorithm);
            IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            cipher.init(Cipher.DECRYPT_MODE, skey, iv);// 初始化
            byte[] result = cipher.doFinal(Base64.decode(content));
            return new String(result); // 解密
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*************************************上海码加密解密************************************/


    /**
     * base64编码
     *
     * @param json json字符串
     * @return
     */
    public static String baseSign(String json) {
        return Base64.encode(json);
    }

    /**
     * base64解码
     *
     * @param json
     * @return
     */
    public static String baseDecrypt(String json) {
        return new String(Base64.decode(json));
    }


    /*************************************粤康码加密解密************************************/

    private static final String CIPHER_TRANSFORMATION_ECB = "AES/ECB/PKCS5PADDING";
    private static final String CIPHER_TRANSFORMATION_CBC = "AES/CBC/PKCS5PADDING";
    private static final String AES_ENCRYPTION_ALGORITHM = "AES";
    private static final String encryptKey = "Pfghjk_@20220101";
    private static final String iv = "thisisarealvector";

    public static String aesEncode(String plainText) {
        Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptKey, iv);
        byte[] cipherText = new byte[0];
        try {
            cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        JavaBase64.Encoder encoder = JavaBase64.getEncoder();
        return new String(encoder.encode(cipherText), StandardCharsets.UTF_8);
    }


    public static String aesDecode(String encryptedText) {
        try {
            Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptKey, iv);
            JavaBase64.Decoder decoder = JavaBase64.getDecoder();
            byte[] cipherText = cipher.doFinal(decoder.decode(encryptedText.getBytes(StandardCharsets.UTF_8)));
            return new String(cipherText, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static String aesUrlEncode(String encryptKey, String plainText) {
        return aesUrlEncode(encryptKey, plainText, null);
    }

    public static String aesUrlEncode(String encryptKey, String plainText, String iv) {
        try {
            Cipher cipher = cipher(Cipher.ENCRYPT_MODE, encryptKey, iv);
            byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            JavaBase64.Encoder encoder = JavaBase64.getUrlEncoder();
            return new String(encoder.encode(cipherText), StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static String aesUrlDecode(String encryptKey, String encryptedText) {
        return aesUrlDecode(encryptKey, encryptedText, null);
    }

    public static String aesUrlDecode(String encryptKey, String encryptedText, String iv) {
        try {
            Cipher cipher = cipher(Cipher.DECRYPT_MODE, encryptKey, iv);
            JavaBase64.Decoder decoder = JavaBase64.getUrlDecoder();
            byte[] cipherText = cipher.doFinal(decoder.decode(encryptedText.getBytes(StandardCharsets.UTF_8)));
            return new String(cipherText, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static Cipher cipher(int mode, String encryptKey, String iv) {
        if (encryptKey.length() > 16) {
            encryptKey = encryptKey.substring(0, 16);
        }
        byte[] key = encryptKey.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES_ENCRYPTION_ALGORITHM);

        Cipher cipher = null;
        if (iv == null) {
            try {
                cipher = Cipher.getInstance(CIPHER_TRANSFORMATION_ECB);
                cipher.init(mode, secretKeySpec);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }

        } else {
            try {
                cipher = Cipher.getInstance(CIPHER_TRANSFORMATION_CBC);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            }
            try {
                cipher.init(mode, secretKeySpec, getIvParameterSpec(iv));
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }
        }
        return cipher;
    }

    public static IvParameterSpec getIvParameterSpec(String iv) {
        final int MAX_SIZE = 16;
        if (iv == null) {
            iv = "";
        }
        byte[] data = new byte[MAX_SIZE];
        byte[] data_str = iv.getBytes();
        for (int i = 0, len = data_str.length; i < MAX_SIZE; ++i) {
            data[i] = (i < len) ? data_str[i] : 0;
        }
        return new IvParameterSpec(data);
    }

    /*************************************粤康码加密解密************************************/

}
